﻿using Xejen.Communication.Configuration;
using Xejen.Communication.Ftp.Abstract;
using Xejen.Communication.Ftp.Configuration;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace Xejen.Communication.Ftp
{
    /// <summary>
    /// 使用<see langword="FtpWebRequest"/>技术向远程服务器发起请求
    /// </summary>
    /// <creator>marc</creator>
    internal class FtpWebReqeustInvoker : IFtpInvoker
    {
        /// <inheritdoc/>
        public FtpConfig Config { get; set; }

        /// <inheritdoc cref="FtpWebReqeustInvoker"/>
        protected FtpWebReqeustInvoker() { }

        /// <inheritdoc cref="FtpWebReqeustInvoker"/>
        public FtpWebReqeustInvoker(FtpConfig config)
        {
            Check.NotNull(config, nameof(config));

            Config = config;
        }

        //private async Task<Stream> GetRequestStreamAsyncWithTimeoutAsync(FtpWebRequest request, int timeout = 4000, CancellationToken cancellationToken = default)
        //{
        //    using (var timeoutCts = new CancellationTokenSource(timeout))
        //    {
        //        using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token))
        //        {
        //            var task = Task.Run(() => request.GetRequestStream(), linkedCts.Token);
        //            await Task.WhenAny(task, Task.Delay(Timeout.Infinite, linkedCts.Token));

        //            if (task.IsCompleted)
        //            {
        //                linkedCts.Token.ThrowIfCancellationRequested();
        //                return await Task.FromResult(task.Result);
        //            }
        //            else
        //            {
        //                linkedCts.Token.ThrowIfCancellationRequested();
        //                throw new TimeoutException("The operation timed out.");
        //            }
        //        }
        //    }
        //}

        //private async Task<FtpWebResponse> GetResponseAsyncWithTimeoutAsync(FtpWebRequest request, int timeout = 4000, CancellationToken cancellationToken = default)
        //{
        //    using (var timeoutCts = new CancellationTokenSource(timeout))
        //    {
        //        using (var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutCts.Token))
        //        {
        //            var task = Task.Run(() => (FtpWebResponse)request.GetResponse(), linkedCts.Token);
        //            await Task.WhenAny(task, Task.Delay(Timeout.Infinite, linkedCts.Token));

        //            if (task.IsCompleted)
        //            {
        //                linkedCts.Token.ThrowIfCancellationRequested();
        //                return task.Result;
        //            }
        //            else
        //            {
        //                linkedCts.Token.ThrowIfCancellationRequested();
        //                throw new TimeoutException("The operation timed out.");
        //            }
        //        }
        //    }
        //}

        /// <inheritdoc/>
        public async Task<FtpResponse> UploadFileAsync<TInput>(FtpAddress address, TInput input, IProgress<IProgressData> progress, int timeout, CancellationToken cancellationToken)
        {
            Uri ftpUri = new Uri($"ftp://{Config.ServerAddress}:{Config.ServerPort}/{address.Path}");

            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUri);
            request.Method = WebRequestMethods.Ftp.UploadFile;

            if (!(string.IsNullOrEmpty(Config.UserName) && string.IsNullOrEmpty(Config.Password)))
            {
                request.Credentials = new NetworkCredential(Config.UserName, Config.Password);
            }

            request.UseBinary = Config.UseBinary;

            Check.NotNull(input, nameof(input));

            string filePath = input.ToString();
            Check.NotNullOrEmpty(filePath, nameof(input));

            if (!File.Exists(filePath))
            {
                throw new FileNotFoundException($"待上传的文件不存在：{filePath}");
            }

            //不报告进度的情况下，处理卡UI界面的问题
            //#if NET462
            //            byte[] dataBytes = null;
            //            await Task.Run(async () =>
            //            {
            //                using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true))
            //                {
            //                    dataBytes = new byte[fileStream.Length];
            //                    await fileStream.ReadAsync(dataBytes, 0, dataBytes.Length, cancellationToken);
            //                }
            //            });
            //#else
            //            byte[] dataBytes = await File.ReadAllBytesAsync(filePath);
            //#endif

            byte[] dataBytes = null;
            long totalBytes = new FileInfo(filePath).Length;

            //开启线程读取本地文件字节流，是为了防止卡住UI界面
            await Task.Run(async () =>
            {
                using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true))
                {
                    byte[] buffer = new byte[1024 * 100];

                    dataBytes = new byte[fileStream.Length];
                    long totalBytesRead = 0;
                    int bytesReadThisRound;
                    int offset = 0;
                    while ((bytesReadThisRound = await fileStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
                    {
                        Buffer.BlockCopy(buffer, 0, dataBytes, offset, bytesReadThisRound);
                        totalBytesRead += bytesReadThisRound;
                        offset += bytesReadThisRound;
                        progress?.Report(new ProgressData(totalBytesRead, totalBytes, "正在读取本地文件字节流"));
                    }
                }
            });

            //不报告进度的写法
            //using (Stream requestStream = await request.GetRequestStreamAsync())
            //{
            //    await requestStream.WriteAsync(dataBytes, 0, dataBytes.Length, cancellationToken);
            //}

            using (Stream requestStream = await request.GetRequestStreamAsync())
            {
                long totalBytesWritten = 0;
                int bufferSize = 1024 * 100;
                byte[] buffer = new byte[bufferSize];

                while (totalBytesWritten < totalBytes)
                {
                    int bytesToWrite = (int)Math.Min(bufferSize, totalBytes - totalBytesWritten);
                    Buffer.BlockCopy(dataBytes, (int)totalBytesWritten, buffer, 0, bytesToWrite);
                    await requestStream.WriteAsync(buffer, 0, bytesToWrite, cancellationToken);
                    totalBytesWritten += bytesToWrite;
                    progress?.Report(new ProgressData(totalBytesWritten, totalBytes, "正在将字节流写入服务器"));
                }
            }

            using (FtpWebResponse response = (FtpWebResponse)await request.GetResponseAsync())
            {
                int statusCode = (int)response.StatusCode;
                string statusDescription = response.StatusDescription;

                // Handle RETR command's response data
                // Example: Read response data from response.GetResponseStream()
                // ... to do something

                return FtpResponse.Success(statusCode, statusDescription);
            }
        }

        /// <inheritdoc/>
        public async Task<FtpResponse> AppendFileAsync(FtpAddress address, string filePath, int timeout, CancellationToken cancellationToken)
        {
            Uri ftpUri = new Uri($"ftp://{Config.ServerAddress}:{Config.ServerPort}/{address.Path}");

            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUri);
            request.Method = WebRequestMethods.Ftp.AppendFile;
            request.Timeout = timeout;

            if (!(string.IsNullOrEmpty(Config.UserName) && string.IsNullOrEmpty(Config.Password)))
            {
                request.Credentials = new NetworkCredential(Config.UserName, Config.Password);
            }

            request.UseBinary = Config.UseBinary;

            Check.NotNullOrEmpty(filePath, nameof(filePath));

            if (!File.Exists(filePath))
            {
                throw new FileNotFoundException($"待添加的文件不存在：{filePath}");
            }

            byte[] dataBytes = File.ReadAllBytes(filePath);

            using (Stream requestStream = await request.GetRequestStreamAsync())
            {
                await requestStream.WriteAsync(dataBytes, 0, dataBytes.Length, cancellationToken);
            }

            using (FtpWebResponse response = (FtpWebResponse)await request.GetResponseAsync())
            {
                int statusCode = (int)response.StatusCode;
                string statusDescription = response.StatusDescription;

                return FtpResponse.Success(statusCode, statusDescription);
            }
        }

        /// <inheritdoc/>
        public async Task<FtpResponse> DownloadFileAsync(FtpAddress address, string localFilePath, IProgress<IProgressData> progress, int timeout, CancellationToken cancellationToken)
        {
            Uri ftpUri = new Uri($"ftp://{Config.ServerAddress}:{Config.ServerPort}/{address.Path}");

            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUri);
            request.Method = WebRequestMethods.Ftp.DownloadFile;
            request.Timeout = timeout;

            if (!(string.IsNullOrEmpty(Config.UserName) && string.IsNullOrEmpty(Config.Password)))
            {
                request.Credentials = new NetworkCredential(Config.UserName, Config.Password);
            }

            request.UseBinary = Config.UseBinary;

            using (FtpWebResponse response = (FtpWebResponse)await request.GetResponseAsync())
            {
                int statusCode = (int)response.StatusCode;
                string statusDescription = response.StatusDescription;

                if (statusCode == 125)
                {
                    using (Stream responseStream = response.GetResponseStream())
                    using (FileStream localFileStream = File.Create(localFilePath))
                    {
                        byte[] buffer = new byte[1024 * 100];
                        int bytesRead;
                        long totalBytesRead = 0;
                        long totalBytes = response.ContentLength;

                        if (totalBytes == -1)
                        {
                            var fileSizeResponse = await GetFileSizeAsync(address, timeout, cancellationToken);
                            totalBytes = fileSizeResponse.ReturnData.Value;
                        }

                        while ((bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken)) > 0)
                        {
                            await localFileStream.WriteAsync(buffer, 0, bytesRead, cancellationToken);

                            totalBytesRead += bytesRead;

                            // 报告下载进度
                            if (progress != null)
                            {
                                var downloadProgress = new ProgressData(totalBytesRead, totalBytes, "正在从服务器下载文件");
                                progress.Report(downloadProgress);
                            }
                        }
                    }
                }

                statusCode = (int)response.StatusCode;
                if (statusCode == 226)
                {
                    return FtpResponse.Success(statusCode, statusDescription);
                }
                else
                {
                    return FtpResponse.Fail(statusCode, "Data transfer failed: " + statusDescription, 201);
                }
            }
        }

        /// <inheritdoc/>
        public async Task<FtpResponse> MakeDirectoryAsync(FtpAddress address, int timeout, CancellationToken cancellationToken)
        {
            Uri ftpUri = new Uri($"ftp://{Config.ServerAddress}:{Config.ServerPort}/{address.Path}");

            string localPath = ftpUri.LocalPath;

            await CreateParentDirectoriesAsync(localPath, timeout, cancellationToken);

            return FtpResponse.Success(257, "All directories created successfully.");
        }

        private async Task CreateParentDirectoriesAsync(string directoryPath, int timeout, CancellationToken cancellationToken)
        {
            if (!string.IsNullOrEmpty(directoryPath))
            {
                if (directoryPath == "/" || directoryPath.IndexOf('/') == -1)
                {
                    return;
                }

                string parentDirectoryPath = directoryPath.Substring(0, directoryPath.LastIndexOf('/'));
                await CreateParentDirectoriesAsync(parentDirectoryPath, timeout, cancellationToken);

                await CreateSingleDirectoryAsync(directoryPath, timeout, cancellationToken);
            }
        }

        private async Task<FtpResponse> CreateSingleDirectoryAsync(string directoryPath, int timeout, CancellationToken cancellationToken)
        {
            Uri ftpUri = new Uri($"ftp://{Config.ServerAddress}:{Config.ServerPort}/{directoryPath}");

            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUri);
            request.Method = WebRequestMethods.Ftp.MakeDirectory;
            request.Timeout = timeout;

            if (!(string.IsNullOrEmpty(Config.UserName) && string.IsNullOrEmpty(Config.Password)))
            {
                request.Credentials = new NetworkCredential(Config.UserName, Config.Password);
            }

            request.UseBinary = Config.UseBinary;

            try
            {
                using (FtpWebResponse response = (FtpWebResponse)await request.GetResponseAsync())
                {
                    int statusCode = (int)response.StatusCode;
                    string statusDescription = response.StatusDescription;

                    return FtpResponse.Success(statusCode, statusDescription);
                }
            }
            catch (WebException exp)
            {
                return HandleException(exp);
            }
        }

        /// <inheritdoc/>
        public async Task<FtpResponse> RenameFileAsync<TInput>(FtpAddress address, TInput input, int timeout, CancellationToken cancellationToken)
        {
            Uri ftpUri = new Uri($"ftp://{Config.ServerAddress}:{Config.ServerPort}/{address.Path}");

            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUri);
            request.Method = WebRequestMethods.Ftp.Rename;
            request.Timeout = timeout;

            if (!(string.IsNullOrEmpty(Config.UserName) && string.IsNullOrEmpty(Config.Password)))
            {
                request.Credentials = new NetworkCredential(Config.UserName, Config.Password);
            }

            request.UseBinary = Config.UseBinary;
            request.RenameTo = input.ToString();

            try
            {
                using (FtpWebResponse response = (FtpWebResponse)await request.GetResponseAsync())
                {
                    int statusCode = (int)response.StatusCode;
                    string statusDescription = response.StatusDescription;

                    return FtpResponse.Success(statusCode, statusDescription);
                }
            }
            catch (WebException exp)
            {
                return HandleException(exp);
            }
        }

        /// <inheritdoc/>
        public async Task<FtpResponse> DeleteFileAsync(FtpAddress address, int timeout, CancellationToken cancellationToken)
        {
            Uri ftpUri = new Uri($"ftp://{Config.ServerAddress}:{Config.ServerPort}/{address.Path}");

            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUri);
            request.Method = WebRequestMethods.Ftp.DeleteFile;
            request.Timeout = timeout;

            if (!(string.IsNullOrEmpty(Config.UserName) && string.IsNullOrEmpty(Config.Password)))
            {
                request.Credentials = new NetworkCredential(Config.UserName, Config.Password);
            }

            request.UseBinary = Config.UseBinary;

            try
            {
                using (FtpWebResponse response = (FtpWebResponse)await request.GetResponseAsync())
                {
                    int statusCode = (int)response.StatusCode;
                    string statusDescription = response.StatusDescription;

                    return FtpResponse.Success(statusCode, statusDescription);
                }
            }
            catch (WebException exp)
            {
                return HandleException(exp);
            }
        }

        /// <inheritdoc/>
        public async Task<FtpResponse<DateTime?>> GetDateTimestampAsync(FtpAddress address, int timeout, CancellationToken cancellationToken)
        {
            Uri ftpUri = new Uri($"ftp://{Config.ServerAddress}:{Config.ServerPort}/{address.Path}");

            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUri);
            request.Method = WebRequestMethods.Ftp.GetDateTimestamp;
            request.Timeout = timeout;

            if (!(string.IsNullOrEmpty(Config.UserName) && string.IsNullOrEmpty(Config.Password)))
            {
                request.Credentials = new NetworkCredential(Config.UserName, Config.Password);
            }

            try
            {
                using (FtpWebResponse response = (FtpWebResponse)await request.GetResponseAsync())
                {
                    int statusCode = (int)response.StatusCode;
                    string statusDescription = response.StatusDescription;

                    DateTime dateTimestamp = response.LastModified;
                    return FtpResponse<DateTime?>.Success(statusCode, statusDescription, dateTimestamp);
                }
            }
            catch (WebException exp)
            {
                return HandleException<DateTime?>(exp);
            }
        }

        /// <inheritdoc/>
        public async Task<FtpResponse<long?>> GetFileSizeAsync(FtpAddress address, int timeout, CancellationToken cancellationToken)
        {
            Uri ftpUri = new Uri($"ftp://{Config.ServerAddress}:{Config.ServerPort}/{address.Path}");

            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUri);
            request.Method = WebRequestMethods.Ftp.GetFileSize;
            request.Timeout = timeout;

            if (!(string.IsNullOrEmpty(Config.UserName) && string.IsNullOrEmpty(Config.Password)))
            {
                request.Credentials = new NetworkCredential(Config.UserName, Config.Password);
            }

            try
            {
                using (FtpWebResponse response = (FtpWebResponse)await request.GetResponseAsync())
                {
                    int statusCode = (int)response.StatusCode;
                    string statusDescription = response.StatusDescription;

                    long fileSize = response.ContentLength;
                    return FtpResponse<long?>.Success(statusCode, statusDescription, fileSize);
                }
            }
            catch (WebException exp)
            {
                return HandleException<long?>(exp);
            }
        }

        /// <inheritdoc/>
        public async Task<FtpResponse<string[]>> ListDirectoryAsync(FtpAddress address, int timeout, CancellationToken cancellationToken)
        {
            Uri ftpUri = new Uri($"ftp://{Config.ServerAddress}:{Config.ServerPort}/{address.Path}");

            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUri);
            request.Method = WebRequestMethods.Ftp.ListDirectory;
            request.Timeout = timeout;

            if (!(string.IsNullOrEmpty(Config.UserName) && string.IsNullOrEmpty(Config.Password)))
            {
                request.Credentials = new NetworkCredential(Config.UserName, Config.Password);
            }

            try
            {
                using (FtpWebResponse response = (FtpWebResponse)await request.GetResponseAsync())
                {
                    int statusCode = (int)response.StatusCode;
                    string statusDescription = response.StatusDescription;

                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        string directoryListing = reader.ReadToEnd();
                        string[] files = directoryListing.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
                        return FtpResponse<string[]>.Success(statusCode, statusDescription, files);
                    }
                }
            }
            catch (WebException exp)
            {
                return HandleException<string[]>(exp);
            }
        }

        /// <inheritdoc/>
        public async Task<FtpResponse<string[]>> ListDirectoryDetailsAsync(FtpAddress address, int timeout, CancellationToken cancellationToken)
        {
            Uri ftpUri = new Uri($"ftp://{Config.ServerAddress}:{Config.ServerPort}/{address.Path}");

            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUri);
            request.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
            request.Timeout = timeout;

            if (!(string.IsNullOrEmpty(Config.UserName) && string.IsNullOrEmpty(Config.Password)))
            {
                request.Credentials = new NetworkCredential(Config.UserName, Config.Password);
            }

            try
            {
                using (FtpWebResponse response = (FtpWebResponse)await request.GetResponseAsync())
                {
                    int statusCode = (int)response.StatusCode;
                    string statusDescription = response.StatusDescription;

                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        string directoryDetails = reader.ReadToEnd();
                        string[] details = directoryDetails.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
                        return FtpResponse<string[]>.Success(statusCode, statusDescription, details);
                    }
                }
            }
            catch (WebException exp)
            {
                return HandleException<string[]>(exp);
            }
        }

        /// <inheritdoc/>
        public async Task<FtpResponse<string>> PrintWorkingDirectoryAsync(FtpAddress address, int timeout, CancellationToken cancellationToken)
        {
            Uri ftpUri = new Uri($"ftp://{Config.ServerAddress}:{Config.ServerPort}/{address.Path}");

            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUri);
            request.Method = WebRequestMethods.Ftp.PrintWorkingDirectory;
            request.Timeout = timeout;

            if (!(string.IsNullOrEmpty(Config.UserName) && string.IsNullOrEmpty(Config.Password)))
            {
                request.Credentials = new NetworkCredential(Config.UserName, Config.Password);
            }

            using (FtpWebResponse response = (FtpWebResponse)await request.GetResponseAsync())
            {
                int statusCode = (int)response.StatusCode;
                string statusDescription = response.StatusDescription;

                string currentDirectory = null;
                int start = statusDescription.IndexOf('"') + 1;
                int end = statusDescription.LastIndexOf('"');
                if (end >= start)
                {
                    currentDirectory = statusDescription.Substring(start, end - start);
                }
                return FtpResponse<string>.Success(statusCode, statusDescription, currentDirectory);
            }
        }

        /// <inheritdoc/>
        public async Task<FtpResponse> RemoveDirectoryAsync(FtpAddress address, int timeout, CancellationToken cancellationToken)
        {
            Uri ftpUri = new Uri($"ftp://{Config.ServerAddress}:{Config.ServerPort}/{address.Path}");

            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUri);
            request.Method = WebRequestMethods.Ftp.RemoveDirectory;
            request.Timeout = timeout;

            if (!(string.IsNullOrEmpty(Config.UserName) && string.IsNullOrEmpty(Config.Password)))
            {
                request.Credentials = new NetworkCredential(Config.UserName, Config.Password);
            }

            try
            {
                using (FtpWebResponse response = (FtpWebResponse)await request.GetResponseAsync())
                {
                    int statusCode = (int)response.StatusCode;
                    string statusDescription = response.StatusDescription;

                    return FtpResponse.Success(statusCode, statusDescription);
                }
            }
            catch (WebException exp)
            {
                return HandleException(exp);
            }
        }

        /// <inheritdoc/>
        public async Task<FtpResponse<string>> UploadFileWithUniqueNameAsync(FtpAddress address, string filePath, int timeout, CancellationToken cancellationToken)
        {
            Uri ftpUri = new Uri($"ftp://{Config.ServerAddress}:{Config.ServerPort}/{address.Path}");

            FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpUri);
            request.Method = WebRequestMethods.Ftp.UploadFileWithUniqueName;
            request.Timeout = timeout;

            if (!(string.IsNullOrEmpty(Config.UserName) && string.IsNullOrEmpty(Config.Password)))
            {
                request.Credentials = new NetworkCredential(Config.UserName, Config.Password);
            }

            request.UseBinary = Config.UseBinary;

            Check.NotNullOrEmpty(filePath, nameof(filePath));

            if (!File.Exists(filePath))
            {
                throw new FileNotFoundException($"待上传的文件不存在：{filePath}");
            }

            byte[] dataBytes = File.ReadAllBytes(filePath);

            try
            {
                using (Stream requestStream = await request.GetRequestStreamAsync())
                {
                    await requestStream.WriteAsync(dataBytes, 0, dataBytes.Length, cancellationToken);
                }
            }
            catch (WebException exp)
            {
                return HandleException<string>(exp);
            }

            using (FtpWebResponse response = (FtpWebResponse)await request.GetResponseAsync())
            {
                int statusCode = (int)response.StatusCode;
                string statusDescription = response.StatusDescription;

                string newFileName = response.Headers["Location"];
                if (string.IsNullOrEmpty(newFileName))
                {
                    using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                    {
                        string responseData = reader.ReadToEnd();
                        newFileName = responseData;
                    }
                }

                string errorMessage = null;
                if (statusCode == 226 && string.IsNullOrEmpty(newFileName))
                {
                    errorMessage = $"已上传成功，但未从服务端上得到关于新文件名的信息，请您自行通过其它方式获取，如ListDirectory命令。";
                }

                return FtpResponse<string>.Success(statusCode, statusDescription, newFileName);
            }
        }

        private FtpResponse HandleException(WebException exp)
        {
            var response = (FtpWebResponse)exp.Response;
            var statusCode = response.StatusCode;
            if (statusCode == FtpStatusCode.ActionNotTakenFileUnavailable)
            {
                Debug.WriteLine($"异常已处理：" + Environment.NewLine + exp);
                return FtpResponse.Fail((int)statusCode, exp.Message, 201);
            }

            Debug.WriteLine(exp);
            throw exp;
        }

        private FtpResponse<T> HandleException<T>(WebException exp)
        {
            var response = (FtpWebResponse)exp.Response;
            var statusCode = response.StatusCode;
            if (statusCode == FtpStatusCode.ActionNotTakenFileUnavailable)
            {
                Debug.WriteLine($"异常已处理：" + Environment.NewLine + exp);
                return FtpResponse<T>.Fail((int)statusCode, exp.Message, 201);
            }

            Debug.WriteLine(exp);
            throw exp;
        }
    }
}
